﻿using System;
using System.ComponentModel;
using Castle.DynamicProxy;
using NBehave.Spec.NUnit;
using NUnit.Framework;
using TimeKeeper.Infrastructure;

namespace TimeKeeper.UnitTests.Infrastructure
{
    public abstract class when_object_is_intercepted_for_INotifyPropertyChanged_interface : Specification
    {
        protected const string OriginalString = "Something";
        protected const string OtherString = "Something else";
        protected const int OriginalInteger = 1;
        protected const int OtherInteger = 2;
        protected readonly Dummy OriginalObject = new Dummy();
        protected readonly Dummy OtherObject = new Dummy();

        protected string Result = null;

        public class Dummy
        {
            public virtual string String { get; set; }
            public virtual int Integer { get; set; }
            public virtual object Object { get; set; }
        }

        protected Dummy UnderTest;

        protected override void Establish_context()
        {
            base.Establish_context();
            var generator = new ProxyGenerator();
            UnderTest = (Dummy)generator.CreateClassProxy(typeof(Dummy), new[] { typeof(INotifyPropertyChanged) }, new PropertyChangedInterceptor());
            UnderTest.String = OriginalString;
            UnderTest.Integer = OriginalInteger;
            UnderTest.Object = OriginalObject;
            ((INotifyPropertyChanged)UnderTest).PropertyChanged += (sender, args) => Result = args.PropertyName;
        }
    }

    public class and_String_property_is_set_to_something_different : when_object_is_intercepted_for_INotifyPropertyChanged_interface
    {

        protected override void Because_of()
        {
            base.Because_of();
            UnderTest.String = OtherString;
        }

        [Test]
        public void then_object_can_be_casted_to_INotifyPropertyChanged()
        {
            (UnderTest is INotifyPropertyChanged).ShouldBeTrue();
        }

        [Test]
        public void then_event_should_be_raised()
        {
            Result.ShouldNotBeNull();
            Result.ShouldEqual("String");
        }
    }

    public class and_String_property_is_set_to_same_value_as_is_already_there : when_object_is_intercepted_for_INotifyPropertyChanged_interface
    {
        protected override void Because_of()
        {
            base.Because_of();
            UnderTest.String = OriginalString;
        }

        [Test]
        public void then_event_should_not_be_raised()
        {
            Result.ShouldBeNull();
        }
    }

    public class and_Integer_property_is_set_to_something_different : when_object_is_intercepted_for_INotifyPropertyChanged_interface
    {

        protected override void Because_of()
        {
            base.Because_of();
            UnderTest.Integer = OtherInteger;
        }

        [Test]
        public void then_event_should_be_raised()
        {
            Result.ShouldNotBeNull();
            Result.ShouldEqual("Integer");
        }
    }

    public class and_Integer_property_is_set_to_same_value_as_is_already_there : when_object_is_intercepted_for_INotifyPropertyChanged_interface
    {
        protected override void Because_of()
        {
            base.Because_of();
            UnderTest.Integer = OriginalInteger;
        }

        [Test]
        public void then_event_should_not_be_raised()
        {
            Result.ShouldBeNull();
        }
    }

    public class and_Object_property_is_set_to_something_different : when_object_is_intercepted_for_INotifyPropertyChanged_interface
    {

        protected override void Because_of()
        {
            base.Because_of();
            UnderTest.Object = OtherObject;
        }

        [Test]
        public void then_event_should_be_raised()
        {
            Result.ShouldNotBeNull();
            Result.ShouldEqual("Object");
        }
    }

    public class and_Object_property_is_set_to_same_value_as_is_already_there : when_object_is_intercepted_for_INotifyPropertyChanged_interface
    {
        protected override void Because_of()
        {
            base.Because_of();
            UnderTest.Object = OriginalObject;
        }

        [Test]
        public void then_event_should_not_be_raised()
        {
            Result.ShouldBeNull();
        }
    }

    //[TestFixture]
    //public class SpeedTest
    //{
    //    public class Dummy
    //    {
    //        public virtual int Integer { get; set; }
    //    }

    //    public class DummyManual : INotifyPropertyChanged
    //    {
    //        private int _integer;
    //        public int Integer
    //        {
    //            get { return _integer; }
    //            set
    //            {
    //                _integer = value;
    //                if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Integer"));
    //            }
    //        }

    //        public event PropertyChangedEventHandler PropertyChanged;
    //    }

    //    [Test]
    //    public void Test()
    //    {
    //        var generator = new ProxyGenerator();

    //        var a = (Dummy)generator.CreateClassProxy(typeof(Dummy), new[] { typeof(INotifyPropertyChanged) }, new PropertyChangedInterceptor());
    //        ((INotifyPropertyChanged)a).PropertyChanged += OnPropertyChanged;

    //        var s1 = new System.Diagnostics.Stopwatch();
    //        s1.Start();
    //        for (int i = 0; i < 1000000; i++)
    //        {
    //            a.Integer = i;
    //        }
    //        s1.Stop();
            
    //        var b = new DummyManual();
    //        ((INotifyPropertyChanged)b).PropertyChanged += OnPropertyChanged;

    //        var s2 = new System.Diagnostics.Stopwatch();
    //        s2.Start();
    //        for (int i = 0; i < 1000000; i++)
    //        {
    //            a.Integer = i;
    //        }
    //        s2.Stop();
    //    }

    //    private void OnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
    //    {
    //    }
    //}
}
